#ifndef _BUFFER_HPP_20120405
#define _BUFFER_HPP_20120405

#include "base.hpp"

namespace mina 
{
	class PoolBufferAlloc;
	class CircleBufferAlloc;

	class Buffer
	{
		public :
			friend class PoolBufferAlloc;
			friend class CircleBufferAlloc;

		public :
			void copy_in(char ch);
			int32_t copy_in(const char * data, int32_t nbytes);
			int32_t copy_in(const Buffer & buffer, int32_t nbytes);
			int32_t copy_in(const std::string & str, int32_t nbytes);


			void copy_out(char & ch);
			int32_t copy_out(char * data, int32_t nbytes);
			int32_t copy_out(std::string & str, int32_t nbytes);
			int32_t copy_out(Buffer & buffer, int32_t nbytes);

			template <typename _T>
			int32_t append(const _T & val, int32_t nbytes);
			
			void append(char ch);

			template <typename _T>
			int32_t move_to(_T & val, int32_t nbytes);

			void move_to(char & ch);

			bool empty() const { return (_consumer >= _producer); }
			bool full() const  { return (_producer >= _end); }

			/// advance _consumer
			int32_t consume(int32_t nbytes);
			/// advance _producer
			int32_t produce(int32_t nbytes);

			int32_t bytes_consumable() const { return (int32_t)(_producer - _consumer); }
			int32_t bytes_producible() const { return (int32_t)(_end - _producer); }

			char * consumer()       { return _consumer; }
			void consumer(char * c) { _consumer = c; }
			char * producer()       { return _producer; }
			void producer(char * p) { _producer = p; }

			/// read nchars from fd. if nchars == -1, read max chars
			int read(int fd, int nbytes = -1);

			std::string to_string() const;

		public :
			char * _start;
			char * _end;
			
			char * _consumer;
			char * _producer;

			Buffer *        _next;

		private :
			Buffer(int32_t size);
			
			/// [first, last)
			Buffer(const char * first, const char * last, int32_t size);
			Buffer(const Buffer & buffer);

			Buffer & operator = (const Buffer & rhs);
			~Buffer();
	};

	template <typename _T>
		int32_t Buffer::append(const _T & val, int32_t nbytes)
		{
			return produce(copy_in(val, nbytes));
		}

	template <typename _T>
		int32_t Buffer::move_to(_T & val, int32_t nbytes)
		{
			return consume(copy_out(val, nbytes));
		}
}

#endif

